package matching;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

import org.jdom2.Content;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;

import com.google.code.chatterbotapi.ChatterBot;
import com.google.code.chatterbotapi.ChatterBotFactory;
import com.google.code.chatterbotapi.ChatterBotSession;
import com.google.code.chatterbotapi.ChatterBotType;

import control.ProcessedInput;
import control.ProcessedOutput;

public class AimlParser {
	// input aiml file
	final String inputFile = "database//aiml.aiml";
	List<Element> list;
	// data set of interactive human
	private Map<String, String> human;

	// constructor
	private AimlParser() {
		human = new HashMap<String, String>();
		list = XMLRead(inputFile);
	}

	// singleton
	private static AimlParser instance = null;

	public static AimlParser getInstance() {
		if (null == instance) {
			instance = new AimlParser();
		}
		return instance;
	}

	public void setHumanProperty(String attr, String value) {
		human.put(attr, value);
	}

	public String getHumanProperty(String attr) {
		String temp = human.get(attr);
		if (null != temp) {
			return temp;
		}
		return User.getInfo(attr);
	}

	public String getBotProperty(String attr) {
		return MyBot.getInfo(attr);
	}

	// -----------------------------------

	public ProcessedOutput getAnswers(ProcessedInput keys) throws IOException {

		ProcessedOutput answer = new ProcessedOutput();
		String response = "";

		// learn
		/*
		 * if (keys.isLearning()) { // TO-DO // learning here learning(null,
		 * null, null);
		 * 
		 * // store and post answer response =
		 * "Thank you, Can you tell me more?"; answer.setExist(true);
		 * answer.setResponse(response); } else
		 */{
			// that sentence
			// match input
			response = getResponse(keys.getInput());

			if (!response.equals("")) {
				answer.setExist(true);
				answer.setResponse(response);
			} else {
				// we must learn
				response = "I don't know about it. Can you explain it to me ?";
				answer.setExist(false);
				answer.setResponse(response);
			}
		}
		return answer;
	}

	// ------------------------------------------------------------
	// ------------------------------------------------------------
	public List<Element> XMLRead(String iFile) {
		SAXBuilder builder = new SAXBuilder();
		File xmlFile = new File(iFile);
		try {

			Document document = (Document) builder.build(xmlFile);
			Element aimlNode = document.getRootElement();

			// find list of categories
			return aimlNode.getChildren(KeyTags.CATEGORY);

		} catch (IOException io) {
			System.out.println(io.getMessage());
		} catch (JDOMException jdomex) {
			System.out.println(jdomex.getMessage());
		}
		return null;
	}

	private int matchPattern(String input, String pattern) {
		String[] a = input.split(" ");
		int sum = 0;
		for (int i = 0; i < a.length; i++) {
			if (pattern.contains(a[i])) {
				sum += 1;
			}
		}
		return sum;
	}

	private String getResponse(String inPattern) throws IOException {
		// patterns
		int max = -10000;
		int temp = 0;
		int index = -1;
		int rIndex = -1;
		// pattern matching
		// for each input string
		String pattern = inPattern.toUpperCase();
		// downward
		for (int i = 0; i < list.size(); ++i) {

			Element categoryNode = (Element) list.get(i);

			String patternString = categoryNode.getChildText(KeyTags.PATTERN)
					.toUpperCase();
			patternString = patternString.replaceAll("\\*", "\\.\\*");
			patternString = patternString.replaceAll("\\_", "\\.");
			if (pattern.matches(patternString)) {
				// get template
				temp = matchPattern(patternString, pattern);
				if (max < temp) {
					max = temp;
					index = i;
				}
			}

		}
		if (-1 == index) {
			return "";
		} else {
			String pString = list.get(index).getChildText(KeyTags.PATTERN);
			String remainPattern = "it";
			if (pString.contains("*")) {
				rIndex = pString.lastIndexOf('*');

			} else if (pString.contains("_")) {
				rIndex = pString.lastIndexOf('*');
			}
			if (rIndex > -1) {
				remainPattern = pattern.substring(rIndex);
			}

			return processTemplate(list.get(index).getChild(KeyTags.TEMPLATE),
					remainPattern);
		}
	}

	public String setTags(Element root) {
		Element innerElement = root.getChild(KeyTags.SET);
		String hAttr = null, value = null;
		// if root has child
		if (null != innerElement) {

			value = setTags(innerElement);
			System.out.println("inner "
					+ innerElement.getAttributeValue(KeyTags.NAME) + " -> "
					+ value);
		} else {
			value = root.getValue();
		}
		hAttr = root.getAttributeValue(KeyTags.NAME);
		setHumanProperty(hAttr, value);
		return value;
	}

	public String processTemplate(Element template, String remString)
			throws IOException {
		// process srai

		List<Element> elist = template.getChildren();
		List<Content> contentlist = template.getContent();
		StringBuffer result = new StringBuffer();
		String hAttr = "";
		String attr = "";
		int eCount = 0;
		for (Content content : contentlist) {
			if (content.getCType().equals(Content.CType.Element)) {
				// bot name
				Element e = elist.get(eCount);
				// System.out.println("E name: " + e.getName());
				switch (e.getName()) {
				case KeyTags.RANDOM:
					List<Element> randomList = e.getChildren(KeyTags.LI);
					Random rand = new Random();
					int n = rand.nextInt(randomList.size());
					result.append(processTemplate(randomList.get(n), remString));
					break;

				case KeyTags.SRAI:
					List<Content> clist = e.getContent();// only STAR
					StringBuffer patternBuffer = new StringBuffer();
					for (Content c : clist) {
						if (c.getCType().equals(Content.CType.Element)) {
							// STAR
							patternBuffer.append("*");
						} else {
							patternBuffer.append(c.getValue());
						}
					}
					System.out.println("buffer: " + patternBuffer.toString());
					result.append(getResponse(patternBuffer.toString().trim()));
					break;
				case KeyTags.BOT:
					// <bot> -> attr.value, <set> attr
					attr = e.getAttributeValue(KeyTags.NAME);
					result.append(getBotProperty(attr));
					break;
				case KeyTags.PERSON:
					// get remain pattern
					result.append(remString);
					break;
				case KeyTags.GET:
					attr = e.getAttributeValue(KeyTags.NAME);
					result.append(getHumanProperty(attr));
					break;
				case KeyTags.SET:
					// if set has nested loop
					setTags(e);
					break;
				case KeyTags.THINK:
					List<Element> thinkList = e.getChildren();
					if (!thinkList.isEmpty()) {
						for (Element setElement : thinkList) {
							setTags(setElement);
						}
					}

					break;
				default:
					break;
				}

				++eCount;

			} else {
				result.append(content.getValue());
			}
		}

		return result.toString().trim();
		/*
		 * process template 0. check if exist srai 1. partition into 3 parts
		 * <srai> 2. part1 + part 3 -> sub template: bot, set, get 3. srai ->
		 * star -> * -> getResponse 4. remove <think>, <set name = it> 5.
		 * replace name
		 */

	}

	public void addToDatabase(String file, String pattern, String response,
			String that) {

		// determine when it is learning
		SAXBuilder builder = new SAXBuilder();
		File xmlFile = new File(file);

		try {
			Document document = (Document) builder.build(xmlFile);
			Element aimlNode = document.getRootElement();

			Element pElement = new Element("pattern").setText(pattern);
			Element thatElement = new Element("that").setText(that);
			Element tElement = new Element("template").setText(response);
			Element cElement = new Element("category");
			cElement.addContent(pElement);
			if (!that.equals(""))
				cElement.addContent(thatElement);
			cElement.addContent(tElement);

			aimlNode.addContent(cElement);

			XMLOutputter xmlOutput = new XMLOutputter();

			xmlOutput.setFormat(Format.getPrettyFormat());
			xmlOutput.output(document, new FileWriter(xmlFile));

		} catch (IOException io) {
			System.out.println(io.getMessage());
		} catch (JDOMException jdomex) {
			System.out.println(jdomex.getMessage());
		}
	}
	
	public String askOnWebsite(String pattern){
		ChatterBotFactory factory = new ChatterBotFactory();
		try {
			ChatterBot bot1 = factory.create(ChatterBotType.JABBERWACKY);
			//ChatterBot bot1 = factory.create(ChatterBotType.CLEVERBOT);
			ChatterBotSession bot1session = bot1.createSession(); 
			String template = bot1session.think(pattern); 

			return template;
		} catch(Exception e) {
			return "Sorry, I cannot help you";
		}
	}

	public void storeHumanInfo(String name) {
		try {

			Element e = new Element(name);
			Document doc = new Document(e);

			for (Map.Entry<String, String> entry : human.entrySet()) {
				Element feature = new Element("category");
				feature.addContent(new Element(entry.getKey()).setText(entry
						.getValue()));
				doc.getRootElement().addContent(feature);
			}

			// new XMLOutputter().output(doc, System.out);
			XMLOutputter xmlOutput = new XMLOutputter();

			// display nice nice
			xmlOutput.setFormat(Format.getPrettyFormat());
			xmlOutput.output(doc, new FileWriter("database//" + name + ".xml"));

		} catch (IOException io) {
			System.out.println(io.getMessage());
		}
	}

	public void loadHumanInfo(String name) {
		List<Element> detailLst = XMLRead("database//" + name + ".xml");
		for (int i = 0; i < detailLst.size(); ++i) {
			Element cNode = (Element) detailLst.get(i);
			String key = cNode.getChildren().get(0).getName();
			String value = cNode.getChildren().get(0).getValue();
			human.put(key, value);
		}
		System.out.println("human = " + human);
	}
}
